package com.stark.commons.lang.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;

import lombok.Cleanup;

/**
 * XML 操作公共方法集合。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
public class XmlUtil {
	
	private static final String SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
	
    /**
     * Schema 校验 xml 文件。
     * @param xmlStr xml 字符串。
     * @param xsdPath xsd 文件路径。
     * @throws IOException
     * @throws SAXException 
     * @see #xmlValidate(Source, String)
     */  
    public static void validateXmlString(String xmlStr, String xsdPath) throws SAXException, IOException {
        @Cleanup InputStream is = new ByteArrayInputStream(xmlStr.getBytes());
        Source source = new StreamSource(is);
        xmlValidate(source, xsdPath);
    }
    
	/**
	 * Schema 校验 xml 文件。
     * @param xmlPath xml 文件路径。
     * @param xsdPath xsd 文件路径。
	 * @throws IOException
	 * @throws SAXException
     * @see #xmlValidate(Source, String)
     */
    public static void validateXmlFile(String xmlPath, String xsdPath) throws SAXException, IOException {
        Source source = new StreamSource(xmlPath);
        xmlValidate(source, xsdPath);
    }
    
    /**
     * Schema 校验 xml 文件。
     * @param source xml 源对象。
     * @param xsdPath xsd 文件路径。
     * @throws SAXException
     * @throws IOException
     */
    private static void xmlValidate(Source source, String xsdPath) throws SAXException, IOException {
    	// 加载schema
        SchemaFactory schemafactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
        File xsdFile = new File(xsdPath);
        Schema schema = schemafactory.newSchema(xsdFile);
		
		// schema校验
        Validator validator = schema.newValidator();
        validator.validate(source);
    }
    
    /** 
     * Javabean 转换成 xml 。
     * <p>默认编码 UTF-8 。
     * @param javabean Javabean 。
     * @return xml 字符串。
     * @throws JAXBException
     * @see #javabeanToXml(Object, String)
     */  
    public static String javabeanToXml(Object javabean) throws JAXBException {
        String xml = javabeanToXml(javabean, "UTF-8");
        return xml;
    }  
  
    /** 
     * Javabean 转换成 xml 。
     * @param javabean Javabean 。
     * @param encoding 字符编码。
     * @return xml 字符串。
     * @throws JAXBException
     */  
    public static String javabeanToXml(Object javabean, String encoding) throws JAXBException {
    	JAXBContext context = JAXBContext.newInstance(javabean.getClass());
    	Marshaller marshaller = context.createMarshaller();
    	marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    	marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
  
    	StringWriter writer = new StringWriter();
    	marshaller.marshal(javabean, writer);
    	String result = writer.toString();
        return result;
    }  
  
    /** 
     * xml 转换成 Javabean 。
     * @param xmlStr xml 字符串。
     * @param clazz Javabean 类型。
     * @return Javabean 。
     * @throws JAXBException
     */  
	public static <T> T xmlToJavaBean(String xmlStr, Class<T> clazz) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(clazz);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		@SuppressWarnings("unchecked")
		T t =  (T) unmarshaller.unmarshal(new StringReader(xmlStr));
		return t;
    }
    
    /**
     * 把 {@link org.dom4j.Document Document} 写入文件。
     * @param document {@link org.dom4j.Document Document} 对象。
     * @param path 文件路径。
     * @throws IOException
     */
    public static void writeXML(Document document, String path) throws IOException {
		FileUtils.createFile(path);
		@Cleanup FileOutputStream out = new FileOutputStream(path);
		OutputFormat of = OutputFormat.createPrettyPrint();
		of.setEncoding("UTF-8");
		@Cleanup XMLWriter writer = new XMLWriter(out, of);
		writer.write(document);
	}
    
}
